/*
 * Copyright 2011 www.pretty-tools.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "stdafx.h"
#include "WindowManager.h"
#include "ResourceResolver.h"

// CWindowManager

CWindowManager::CWindowManager()
{
    ATLTRACE( _T("CWindowManager::CWindowManager()\n") );
}

CWindowManager::~CWindowManager()
{
    ATLTRACE( _T("CWindowManager::~CWindowManager()\n") );
}

STDMETHODIMP CWindowManager::resolveURL(BSTR url, BSTR* result)
{
    if (!url)
        return E_INVALIDARG;
    if (!result)
        return E_POINTER;

    ATLTRACE( L"CWindowManager::resolveURL(\"%s\")\n", url );

    CStringW strResult;
    RETURN_FAILED(ResourceResolver::ResolveURL(url, strResult))

    ATLTRACE( L"CWindowManager::resolveURL(...) -> %s\n", strResult );

    *result = CComBSTR(strResult).Detach();

    return S_OK;
}

int CWindowManager::CountParameters(VARIANT* pParameters)
{
    for (int i = 9; i >= 0; --i)
    {
        if (VT_EMPTY != (pParameters+i)->vt && VT_ERROR != (pParameters+i)->vt)
            return i+1;
    }
    return 0;
}

HRESULT CWindowManager::GetParametersAsVariant(VARIANT* pParameters, CComVariant& parameters)
{
    int nParameterCount = CountParameters(pParameters);
    if (!nParameterCount)
        return S_FALSE;

    CComSafeArray<VARIANT> arParameters;
    RETURN_FAILED(arParameters.Create(nParameterCount))

    for (int i = 0; i < nParameterCount; ++i)
    {
        arParameters[i] = *(pParameters+i);
    }

    parameters.vt = VT_ARRAY | VT_VARIANT;
	parameters.parray = arParameters.Detach();

    return S_OK;
}

STDMETHODIMP CWindowManager::openDialog(BSTR url, LONG width, LONG height, VARIANT param0, VARIANT param1, VARIANT param2, VARIANT param3, VARIANT param4, VARIANT param5, VARIANT param6, VARIANT param7, VARIANT param8, VARIANT param9)
{
    return OpenDialog(url, width, height, true, param0, param1, param2, param3, param4, param5, param6, param7, param8, param9);
}

STDMETHODIMP CWindowManager::openFullScreen(BSTR url, VARIANT param0, VARIANT param1, VARIANT param2, VARIANT param3, VARIANT param4, VARIANT param5, VARIANT param6, VARIANT param7, VARIANT param8, VARIANT param9)
{
    return OpenDialog(url, -1, -1, true, param0, param1, param2, param3, param4, param5, param6, param7, param8, param9);
}

STDMETHODIMP CWindowManager::openHiddenDialog(BSTR url, VARIANT param0, VARIANT param1, VARIANT param2, VARIANT param3, VARIANT param4, VARIANT param5, VARIANT param6, VARIANT param7, VARIANT param8, VARIANT param9)
{
    return OpenDialog(url, 0, 0, false, param0, param1, param2, param3, param4, param5, param6, param7, param8, param9);
}

HRESULT CWindowManager::OpenDialog(BSTR url, LONG width, LONG height, bool bShow, VARIANT param0, VARIANT param1, VARIANT param2, VARIANT param3, VARIANT param4, VARIANT param5, VARIANT param6, VARIANT param7, VARIANT param8, VARIANT param9)
{
    if (!url)
        return E_INVALIDARG;

    ATLTRACE( L"CWindowManager::OpenDialog(\"%s\")\n", url );

    CComBSTR bsURL;
    RETURN_FAILED(resolveURL(url, &bsURL))

    if (ResourceResolver::GetProtocol(bsURL) != _T("res")) // restrict to local resources only
        return E_INVALIDARG;

    WEBBrowser browser;
	RETURN_FAILED(browser.CreateInstance())

    browser.HideAllBars();
    if (width == -1 && height == -1)
	    browser.FullScreen = true;
    else if (width > 0 && height > 0)
    {
		browser.Left = (::GetSystemMetrics(SM_CXSCREEN) - width)/2;
		browser.Width = width;
		browser.Top = (::GetSystemMetrics(SM_CYSCREEN)-height)/2;
		browser.Height = height;
    }

    CComVariant parameters;
    RETURN_FAILED(GetParametersAsVariant(&param0, parameters))

    CComPtr<IArgumentTransfer> transfer;
    if (parameters.vt != VT_EMPTY && FAILED(transfer.CoCreateInstance(CLSID_ArgumentTransfer)))
        return E_FAIL;

    if (transfer) transfer->Attach(browser, parameters);

	if (FAILED(browser.Navigate(bsURL, false)))
    {
        if (transfer) transfer->Detach();
    }
	else if (bShow)
	{
		browser.Visible = true;
#ifdef _SET_IE_FOREGROUND
		HWND hwnd = browser.hwnd;
		if (hwnd && ::IsWindow(hwnd))
		{
			SetForegroundWindow(hwnd);
		}
#endif//_SET_IE_FOREGROUND
	}
 
    return S_OK;
}

STDMETHODIMP CWindowManager::close(IDispatch* pDispWnd)
{
    if (!pDispWnd)
        return E_INVALIDARG;

    HTML::Window wndTop(HTML::Window(pDispWnd).top);
    if (!wndTop)
        return E_INVALIDARG;

    CWindow wnd = wndTop.hwnd;
    if (!wnd)
        wnd = GetDialogWindow();

    if (wnd && wnd.IsWindow() && wnd.PostMessage(WM_SYSCOMMAND, SC_CLOSE))
        return S_OK;

    return wndTop->close();
}

STDMETHODIMP CWindowManager::showModalDialog(IDispatch* wnd, BSTR dialog, VARIANT* varArgIn, VARIANT* varOptions, VARIANT* varArgOut)
{
    CComBSTR bsURL;
    RETURN_FAILED(resolveURL(dialog, &bsURL))

    return HTML::Window(wnd).showModalDialog(bsURL,varArgIn,varOptions,varArgOut);
}

CWindow CWindowManager::GetDialogWindow() const
{
    CWindow wnd = GetActiveWindow();
    if (!wnd)
        return CWindow();

    TCHAR szBuf[128];
    if (!GetClassName(wnd, szBuf, sizeof(szBuf)/sizeof(TCHAR)))
        return CWindow();

    if ( _tcscmp(szBuf, _T("Internet Explorer_TridentDlgFrame")) )
        return CWindow();

    return wnd;
}

STDMETHODIMP CWindowManager::clearDialogTitle(IDispatch* pWnd)
{
    if (!pWnd)
        return E_INVALIDARG;

    HTML::Window htmlWnd(pWnd);
    if (!htmlWnd)
        return S_OK;

    CComBSTR bsTitle = htmlWnd.document.title;
    if (!bsTitle)
        return S_OK;

    CWindow wnd = GetDialogWindow();
    if (!wnd)
        return S_OK;

    wnd.SetWindowText(bsTitle);

    return S_OK;
}

STDMETHODIMP CWindowManager::setDialogStatus(IDispatch* pWnd, BSTR bsStatus)
{
    if (!pWnd)
        return E_INVALIDARG;

    HTML::Window htmlWnd(pWnd);
    htmlWnd.defaultStatus = bsStatus;
    htmlWnd.status = bsStatus;

    CWindow wnd = GetDialogWindow();
    if (!wnd)
        return S_OK;

    CWindow wndStatus = FindWindowEx(wnd, NULL, _T("msctls_statusbar32"), NULL);
    if (!wndStatus)
        return S_OK;

    wndStatus.SetWindowText(bsStatus);
   
    return S_OK;
}

namespace {
    class CKeyboardInput : public INPUT
    {
    public:
        CKeyboardInput(WORD wVk, bool bKeyUp = false)
        {
            ZeroMemory(this, sizeof(CKeyboardInput));
            type = INPUT_KEYBOARD;
            ki.wVk = wVk;
            if (bKeyUp) ki.dwFlags = KEYEVENTF_KEYUP;
            ki.dwExtraInfo = GetMessageExtraInfo();
        }

        CKeyboardInput(WORD wVk, DWORD time, bool bKeyUp = false)
        {
            ZeroMemory(this, sizeof(CKeyboardInput));
            type = INPUT_KEYBOARD;
            ki.wVk = wVk;
            ki.time = time;
            if (bKeyUp) ki.dwFlags = KEYEVENTF_KEYUP;
            ki.dwExtraInfo = GetMessageExtraInfo();
        }
    };
}

#define CONTROL_KEY_TIME    ((DWORD)4000)

STDMETHODIMP CWindowManager::ResendKey(IDispatch* pWnd, LONG keyCode, VARIANT_BOOL shiftKey, VARIANT_BOOL ctrlKey, VARIANT_BOOL altKey)
{
    CSimpleArray<CKeyboardInput> keys;

    if (ctrlKey == VARIANT_TRUE)  keys.Add(CKeyboardInput(VK_CONTROL, CONTROL_KEY_TIME));
    if (shiftKey == VARIANT_TRUE) keys.Add(CKeyboardInput(VK_SHIFT, CONTROL_KEY_TIME));
    if (altKey == VARIANT_TRUE)   keys.Add(CKeyboardInput(VK_MENU, CONTROL_KEY_TIME));
    keys.Add(CKeyboardInput(keyCode));

    RETURN_HRESULT(SendInput(keys.GetSize(), (INPUT*)keys.GetData(), sizeof(CKeyboardInput)));
}

STDMETHODIMP CWindowManager::StopKey(IDispatch* pWnd, LONG keyCode, VARIANT_BOOL shiftKey, VARIANT_BOOL ctrlKey, VARIANT_BOOL altKey)
{
    CSimpleArray<CKeyboardInput> keys;

    if (ctrlKey == VARIANT_TRUE)  keys.Add(CKeyboardInput(VK_CONTROL, true));
    if (shiftKey == VARIANT_TRUE) keys.Add(CKeyboardInput(VK_SHIFT, true));
    if (altKey == VARIANT_TRUE)   keys.Add(CKeyboardInput(VK_MENU, true));
    keys.Add(CKeyboardInput(keyCode, true));

    RETURN_HRESULT(SendInput(keys.GetSize(), (INPUT*)keys.GetData(), sizeof(CKeyboardInput)));
}

STDMETHODIMP CWindowManager::get_XMLHttpRequest(IDispatch** pVal)
{
    if (!pVal)
        return E_POINTER;

    *pVal = NULL;

    CComPtr<IDispatch> pXMLHttpRequest;
    if (FAILED(pXMLHttpRequest.CoCreateInstance(L"Msxml2.XMLHTTP")))
    {
        RETURN_FAILED(pXMLHttpRequest.CoCreateInstance(L"Microsoft.XMLHTTP"))
    }

    *pVal = pXMLHttpRequest.Detach();

    return S_OK;
}
